#ifndef _LIOBASE_H
#define _LIOBASE_H

#include "liodebug.h"
#include <fcntl.h>									// Microsoft File Definitions
#include <io.h>										// Low-Level Input Output Support
#include <gl/gl.h>									// Header File For The OpenGL32 Library
#include <gl/glu.h>									// Header File For The GLu32 Library

#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL (WM_MOUSELAST+1)
#endif
#ifndef WHEEL_DELTA
#define WHEEL_DELTA                     120     /* Value for rolling one detent */
#endif

#ifndef WM_TOGGLEFULLSCREEN							// Application Define Message For Toggling
#define WM_TOGGLEFULLSCREEN (WM_USER+1)									
#endif

#define WM_USER_CALL (WM_USER+2)

#define WM_USER_WAKE (WM_USER+3)

#ifndef IDC_HAND
#define IDC_HAND MAKEINTRESOURCE(32649)
#endif

#if !defined(_W64)
#if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
#define _W64 __w64
#else
#define _W64
#endif
#endif
#ifndef _UINTPTR_T_DEFINED
#ifdef  _WIN64
typedef unsigned __int64    uintptr_t;
#else
typedef _W64 unsigned int   uintptr_t;
#endif
#define _UINTPTR_T_DEFINED
#endif
#ifndef _INTPTR_T_DEFINED
#ifdef  _WIN64
typedef __int64             intptr_t;
#else
typedef _W64 int            intptr_t;
#endif
#define _INTPTR_T_DEFINED
#endif

#ifndef VK_OEM_PLUS
#define VK_OEM_PLUS       0xBB   // '+' any country
#define VK_OEM_COMMA      0xBC   // ',' any country
#define VK_OEM_MINUS      0xBD   // '-' any country
#define VK_OEM_PERIOD     0xBE   // '.' any country
#endif

#ifndef CDS_FULLSCREEN								// CDS_FULLSCREEN Is Not Defined By Some
#define CDS_FULLSCREEN 4							// Compilers. By Defining It This Way,
#endif

namespace Lio {

enum InputTypes{
	NO_INPUT_MSG,
	MOUSE_INPUT_MSG,
	KEY_INPUT_MSG
};											// We Can Avoid Errors	

void beep();
void message(CSTR text, ...);
BOOL question(CSTR text, ...);
CSTR getLastErrorStr(DWORD error);
inline CSTR getLastErrorStr(){return getLastErrorStr(GetLastError());}
HCURSOR setCursor(CSTR name);

struct Mouse{
	BOOL				lbutton;					// Mouse Left Button
	BOOL				mbutton;					// Mouse Middle Button
	BOOL				rbutton;					// Mouse Right Button
	WORD				x;							// Mouse X Position	
	WORD				y;							// Mouse Y Position
	short				wheel;						// Mouse Wheel
	BOOL				shift;						// Shift Button Pressed While Mouse Event
	BOOL				ctrl;						// Control Button Pressed While Mouse Event
	BOOL				move;						// Is the mouse moving
};

struct Keyboard{	
	BOOL				keys[256];					// Keyboard Buttons
	_TCHAR				symbol;						// Ascii Character Of The Last Button
	BYTE				code;						// Virtual Key Code Of The Last Button Pressed (VK)
	SHORT				repeatCount;				// repeat count
	BYTE				scanCode;					// scan code
	BOOL				extendKey;					// extended-key flag
	BOOL				alt;						// context code
	BOOL				repeated;					// previous key-state flag
	BOOL				pressed;					// transition-state flag
	BOOL				waiting;					// Indicates symbol waiting
};

struct Window{
	CSTR				title;						// Window Title
	HWND				hWnd;						// Window Handle
	HDC					hDC;						// Device Context
	HGLRC				hRC;						// Rendering Context
	LONG				left;						// Left Position
	LONG				top;						// Top Position
	LONG				width;						// Width
	LONG				height;						// Height
	BOOL				fullscreen;					// FullScreen?
	BOOL				visible;					// Window Visible?
	RECT				rect;						// oustide borders
	DWORD				style;
	DWORD				extStyle;
};

struct Display{									// Contains Information Vital To A Window
	BYTE bpp;				// Bits Per Pixel
	BYTE depth;				// Number Of Bits For The Depth Buffer
	BYTE stencil;			// Number Of Bits For The Stencil Buffer
	WORD width;				// Resolution X
	WORD height;			// Resolution Y
};

class DLLManager
{
protected:
	HINSTANCE hinstLib;
	//Vector<const char*> funcNames;
public:
	DLLManager():hinstLib(NULL){}
	DLLManager(CSTR libName)
	{
		hinstLib = LoadLibrary(libName);
	}
	~DLLManager(){destroy();}
	BOOL load(CSTR libName)
	{
		destroy();
		return (hinstLib = LoadLibrary(libName))!=NULL;
	}
#if WINVER>=0x0501
	BOOL setDir(CSTR dirName)
	{
        return SetDllDirectory(dirName);
	}
#endif
	FARPROC getFunc(const char* funcName)
	{
		return (hinstLib)?GetProcAddress(hinstLib, funcName):NULL;
	}
	void destroy()
	{
		if(hinstLib)
		{
			FreeLibrary(hinstLib);
			hinstLib=NULL;
		}
	}
};
class Console
{
protected:
	BOOL console,files;
	int dOutput,dInput,dError;
public:
	HANDLE hOutput,hInput,hError;
	FILE *fOutput,*fInput,*fError;
	CONSOLE_SCREEN_BUFFER_INFO coninfo;
	Console():console(FALSE){close();}
	~Console(){close();}
	BOOL open(CSTR title = NULL, WORD rows = 0, WORD cols = 0);
	void close();
};

class Listener
{
public:
	virtual BOOL listen(WORD, WORD, const void *){return FALSE;}
};
class Caller
{
private:
	static WORD CALLID;
	Caller(const Caller &):id(0){} // avoid copying caller objects
public:
	const WORD id;
	Caller():id(++CALLID){assert(CALLID);}
	BOOL call(Listener *listener, WORD code = 0, const void *info = NULL)
	{
		return listener?listener->listen(id,code,info):FALSE;
	}
};
BOOL changeResolution(WORD width, WORD height, BYTE bpp);

template <typename T>
struct ListObject
{
	T value;
	ListObject<T> *next,*prev;
};
template <typename T>
class List
{
public:
	typedef ListObject<T>* Object;
	Object first,last;
	int length;
	List():first(NULL),last(NULL),length(0)
	{}
	List(const List<T> &l):	first(NULL),last(NULL),length(0)
	{
		copy(l);
	}
	~List()
	{
		destroy();
	}
	void destroy()
	{
		if(length)setSize(0);
	}
	BOOL setSize(int newLength)
	{
		int i,count;
		Object start,end;
		if(!newLength)
		{
			start=first;
			while(start)
			{
				end=start;
				start=start->next;
				DELETE_OBJECT(end);
			}
			length=0;
			first=NULL;
			last=NULL;
			return TRUE;
		}
		if(newLength<0)
			return FALSE;
		count=newLength-length;
		if(count)
		{
			if(count>0)
			{
				Object obj;
				if(!NEW_OBJECT(end,ListObject<T>))return FALSE;
				start=end;
				for(i=1;i<count;i++)
				{
					obj=end;
					if(!NEW_OBJECT(end,ListObject<T>))
					{
						while(i)
						{
							end=obj->prev;
							DELETE_OBJECT(obj);
							obj=end;
							i--;
						}
						return NULL;
					}
					end->prev=obj;
					obj->next=end;
				}
				start->prev=last;
				if(last)last->next=start;
				else first=start;
			}
			else
			{
				end=last;
				for(i=0;i>count;i--)
				{
					start=end->prev;
					DELETE_OBJECT(end);
					end=start;
				}
			}
			end->next=NULL;
			last=end;
			length=newLength;
		}
		_TEST(test());
		return TRUE;
	}
	Object objectAt(int position) const
	{
		if(position<0||position>=length)
			return NULL;
		int i;
		Object obj;
		if(position<=(length>>1))
		{
			obj=first;
			i=0;
			while(obj)
			{
				if(i==position)return obj;
				obj=obj->next;
				i++;
			}
		}
		else
		{
			obj=last;
			i=length-1;
			while(obj)
			{
				if(i==position)return obj;
				obj=obj->prev;
				i--;
			}
		}
		return NULL;
	}
	Object insert(const Object next = NULL)
	{
		Object obj,prev;
		if(!NEW_OBJECT(obj,ListObject<T>))
			return NULL;
		if(next)
		{
			obj->next=next;
			obj->prev=prev=next->prev;
			next->prev=obj;
		}
		else
		{
			obj->next=NULL;
			obj->prev=prev=last;
			last=obj;
		}
		if(prev)
			prev->next=obj;
		else
			first=obj;
		length++;
		_TEST(test());
		return obj;
	}
	Object insert(int index)
	{
		return insert((index>=0&&index<length)?objectAt(index):NULL);
	}
	Object add(const T& value, int index)
	{
		Object obj=insert(index);
		if(obj)obj->value=value;
		return obj;
	}
	Object add(const T& value, const Object next = NULL)
	{
		Object obj=insert(next);
		if(obj)obj->value=value;
		return obj;
	}
	T* add()
	{
		Object obj=insert();
		if(!obj)return NULL;
		return &(obj->value);
	}
	BOOL remove(int index = -1)
	{
		if(index<0)
			return remove(last);
		if(!index)
			return remove(first);
		return remove(objectAt(index));
	}
	BOOL remove(Object obj)
	{
		if(!obj)return FALSE;
		if(last==obj)last=obj->prev;
		if(first==obj)first=obj->next;
		if(obj->prev)obj->prev->next=obj->next;
		if(obj->next)obj->next->prev=obj->prev;
		DELETE_OBJECT(obj);
		length--;
		_TEST(test());
		return TRUE;
	}
	BOOL copy(const T *list, int count)
	{
		if(list&&setSize(count))
		{
			int i;
			Object obj;
			for(i=0,obj=first;obj;i++,obj=obj->next)
				obj->value=list[i];
			return TRUE;
		}
		return FALSE;
	}
	int copy(const List<T>& list)
	{
		Object obj1,obj2;
		if(&list==this||!setSize(list.length))return 0;
		for(obj1=first,obj2=list.first;obj2;obj1=obj1->next,obj2=obj2->next)
			obj1->value=obj2->value;
		return TRUE;
	}
	List<T>& operator =(const List<T> &l)
	{
		copy(l);
		return *this;
	}
	void transfer(List<T>& dest)
	{
		dest.destroy();
		dest.first=first;
		dest.last=last;
		dest.length=length;
		length=0;
		first=NULL;
		last=NULL;
	}
	BOOL replace(Object obj1, Object obj2)
	{
		if(!obj1||!obj2)
			return FALSE;
		if(obj1==obj2)
			return TRUE;
		Object
			next1=obj1->next,
			next2=obj2->next,
			prev1=obj1->prev,
			prev2=obj2->prev;
		if(next1==obj2)
		{
			__insert(obj2,prev1,obj1);
			__insert(obj1,obj2,next2);
		}
		else if(next2==obj1)
		{
			__insert(obj2,obj1,next1);
			__insert(obj1,prev2,obj2);
		}
		else
		{
			__insert(obj2,prev1,next1);
			__insert(obj1,prev2,next2);
		}
		_TEST(test());
		return TRUE;
	}
	void test() const
	{
		if(!first)return;
		Object obj;
		int i;
		for(i=0,obj=first;i<length&&obj;i++,obj=obj->next)
			if(obj->next)
				assert(obj==obj->next->prev);
		assert(i==length);
		assert(!obj);
	}
	/*
	BOOL equals(const List<T>& list)const
	{
		if(&list==this)return TRUE;
		if(length!=list.length)return FALSE;
		Object obj1,obj2;
		for(obj1=first,obj2=list.first;obj2;obj1=obj1->next,obj2=obj2->next)
			if(!(obj1->value==obj2->value))return FALSE;
		return TRUE;
	}
	BOOL operator ==(const List<T> &l){return equals(l);}
	BOOL operator !=(const List<T> &l){return !equals(l);}
	*/
	T& operator[](int i)
	{
		Object obj=objectAt(i);
		if(!obj)throw OUT_OF_BOUNDS_ERROR;
		return obj->value;
	}
	const T& operator[](int i) const
	{
		Object obj=objectAt(i);
		if(!obj)throw OUT_OF_BOUNDS_ERROR;
		return obj->value;
	}
	List<T>& operator +=(T value){add(value);return *this;}
	T* operator ++(){return add();}
	BOOL operator --(){return remove();}
	
protected:
	void __insert(Object obj, const Object prev = NULL, const Object next = NULL)
	{
		obj->prev=prev;
		obj->next=next;
		if(next)next->prev=obj;
		else last=obj;
		if(prev)prev->next=obj;
		else first=obj;
	}
};
template <typename T>
class Iterator
{
public:
	const List<T> *list;
	ListObject<T> *obj;
	T* value;
	Iterator(const List<T> *list = NULL):list(list),obj(NULL),value(NULL)
	{}
	T* next()
	{
		if(!list)return NULL;
		obj=obj?obj->next:list->first;
		value=obj?&obj->value:NULL;
		return value;
	}
	void reset()
	{
		value=obj=NULL;
	}
};

template <typename T>
class Vector
{
protected:
	int current_type,current_align,memory;
	void __init(int memType, int memAlign)
	{
		shrink=TRUE;
		type=memType;
		align=memAlign;
		current_type=MEM_TYPE_EXTERN;
		current_align=0;
		elems=NULL;
		memory=size=0;
	}
public:
	BOOL shrink;
	T *elems;
	int size,type,align;
	Vector()
	{
		__init(MEM_TYPE_NEW,0);
	}
	Vector(int memType, int memAlign = 0)
	{
		__init(memType, memAlign);
	}
	Vector(const Vector<T> &v)
	{
		__init(v.type, v.align);
		copy(v);
	}
	~Vector()
	{
		destroy();
	}
	void setAlloc(int memType = MEM_TYPE_NEW, int memAlign = 0)
	{
		type=memType;
		align=memAlign;
	}
	void destroy()
	{
		if(elems)switch(current_type)
		{
			case MEM_TYPE_NEW:
				DELETE_ARRAY(elems);
				break;
			case MEM_TYPE_ALLOC:
				if(current_align)
					DELETE_ALIGNED(elems);
				else
					DELETE_MEMORY(elems);
				break;
		}
		elems=NULL;
		size=0;
		memory=0;
	}
	T* setSize(int newSize, int memOper = MEM_OP_NONE)
	{
		if(newSize<0)
			return NULL;
		if(newSize!=size||current_type!=type||current_align!=align)
		{
			if(shrink||newSize>size||current_type!=type||current_align!=align)
			{
				T* newBuffer=NULL;
				switch(type)
				{
				case MEM_TYPE_NEW:
					NEW_ARRAY(newBuffer,T,newSize);
					break;
				case MEM_TYPE_ALLOC:
					if(align)
						NEW_ALIGNED(newBuffer,T,newSize*sizeof(T),align);
					else
						NEW_MEMORY(newBuffer,T,newSize*sizeof(T));
					break;
				}
				if(!newBuffer)
					return NULL;
				if(memOper==MEM_OP_KEEP)
					memcpy(newBuffer,elems,min(newSize,size)*sizeof(T));
				destroy();
				current_align=align;
				current_type=type;
				elems=newBuffer;
				memory=newSize*sizeof(T);
			}
			size=newSize;
		}
		if(memOper==MEM_OP_CLEAR)
			memset(elems,0,size*sizeof(T));
		return elems;
	}
	void shift(int positions = -1)
	{
		if(!size)return;
		if(positions<0)
		{
			if(size<=-positions)return;
			memmove(elems,elems-positions,(size+positions)*sizeof(T));
		}
		else if(positions>0)
		{
			if(size<=positions)return;
			memmove(elems+positions,elems,(size-positions)*sizeof(T));
		}
	}
	BOOL copy(const T *vector, int length, BOOL resize = TRUE)
	{
		if(!vector||(resize&&!setSize(length)))
			return FALSE;
		memcpy(elems,vector,size*sizeof(T));
		return TRUE;
	}
	BOOL copy(const Vector<T> &vector, BOOL resize = TRUE){return copy(vector.elems,vector.size,resize);}
	BOOL copy(const T *vector){return copy(vector,size,FALSE);}
	Vector<T>& operator =(const Vector<T> &v){copy(v);return *this;}

	BOOL equals(const T *vector, int length, BOOL check_size = TRUE)const
	{
		if(!vector)
			return FALSE;
		else if(!check_size)
			length=min(length,size);
		else if(length!=size)
			return FALSE;
		return !memcmp(elems,vector,length*sizeof(T));
	}
	BOOL equals(const T *vector)const{return equals(vector,size);}
	BOOL equals(const Vector<T> &vector)const{return equals(vector.elems,vector.size);}
	BOOL operator ==(const Vector<T> &v){return equals(v);}
	BOOL operator !=(const Vector<T> &v){return !equals(v);}

	BOOL bind(Vector<T> &vector)
	{
		if(!bind(vector.elems, vector.size, vector.current_type, vector.current_align))
			return FALSE;
		vector.current_type=MEM_TYPE_EXTERN; //avoid both vectors to free the same memory
		return TRUE;
	}
	BOOL bind(T *newBuffer, int newSize, int newType = MEM_TYPE_EXTERN, int newAlign = 0)
	{
		if(newSize<0||!newBuffer)
			return FALSE;
		destroy();
		current_type=newType;
		current_align=newAlign;
		elems=newBuffer;
		size=newSize;
		return TRUE;
	}
	void move(int positions)
	{
		if(positions>0)
		{
			if(positions<size)
				memmove(elems+positions,elems,size-positions);
		}
		else
		{
			if(-positions<size)
				memmove(elems,elems-positions,size+positions);
		}
	}
	size_t write(FILE *file)const{return fwrite(elems,sizeof(T),size,file);}
	size_t read(FILE *file){return fread(elems,sizeof(T),size,file);}
	void clear(){ClearMemory(elems,size*sizeof(T));}
	void setValue(int value){memset(elems,value,size*sizeof(T));}
	operator const T*()const{return elems;}
	operator T*(){return elems;}
	BOOL check(int index)const{return (index>=0&&index<size);}
	void print(CSTR format, FILE * file = stdout)
	{
		if(!format)return;
		for(int i=0;i<size;i++)
			_ftprintf(file,format,elems[i]);
	}
};

template <typename T>
class Table
{
protected:
	Vector<T> buffer;
public:
	T ** elems;
	int rows,cols;

	Table():rows(0),cols(0),elems(NULL)
	{}
	Table(int memType, int memAlign = 0):rows(0),cols(0),elems(NULL),buffer(memType,memAlign)
	{}
	Table(const Table<T> &t):rows(0),cols(0),elems(NULL),buffer(t.buffer.type,t.buffer.align)
	{
		copy(t);
	}
	~Table()
	{
		destroy();
	}
	void destroy()
	{
		buffer.destroy();
		if(elems)DELETE_ARRAY(elems);
		elems=NULL;
		cols=0;
		rows=0;
	}
	T ** setSize(int rowCount, int colCount, int memOper = MEM_OP_NONE)
	{
		if(colCount<0||rowCount<0)
			return NULL;
		
		T **newElems=NULL;
		if(colCount!=cols||rowCount!=rows)
		{
			// create the new lines
			if(!NEW_ARRAY(newElems,T*,rowCount))
				return NULL;
		}

		// create the new values if needed
		if(!buffer.setSize(rowCount*colCount,memOper))
		{
			if(newElems)DELETE_ARRAY(newElems);
			return NULL;
		}

		if(newElems)
		{
			if(elems)DELETE_ARRAY(elems);
			elems=newElems;
			rows=rowCount;
			cols=colCount;
		}
		
		// assign the new lines:
		int i;
		T *ptr;
		for(i=0,ptr=buffer;i<rows;i++,ptr+=cols)
			elems[i]=ptr;

		return elems;
	}

	BOOL copy(const Table<T> &t)
	{
		if(!setSize(t.rows,t.cols))
			return FALSE;
		buffer.copy(t.buffer);
		return TRUE;
	}
	BOOL copy(const T *const* table, int rowCount, int colCount)
	{
		if(!table||!setSize(rowCount,colCount))
			return FALSE;
		for(int i=0;i<rows;i++)
			memcpy(elems[i],table[i],cols*sizeof(T));
		return TRUE;
	}
	BOOL copy(const T *const* table)
	{
		if(!table)
			return FALSE;
		for(int i=0;i<rows;i++)
			memcpy(elems[i],table[i],cols*sizeof(T));
		return TRUE;
	}
	
	BOOL equals(const Table<T> &t)const
	{
		if(rows!=t.rows||cols!=t.cols)
			return FALSE;
		return buffer.equals(t.buffer);
	}
	BOOL equals(const T *const* table, int rowCount, int colCount)const
	{
		if(!table||rowCount!=rows||colCount!=cols)
			return FALSE;
		for(int i=0;i<rowCount;i++)
			if(memcmp(elems[i],table[i],colCount*sizeof(T)))
				return FALSE;
		return TRUE;
	}
	void clear(){buffer.clear();}
	// OPERATORS:
	operator T**(){return elems;}
	operator const T*const*()const{return elems;}
	Table& operator =(const Table &t){copy(t);return *this;}
	Table& operator =(const T *const* table){copy(t);return *this;}
	BOOL operator ==(const Table &t)const{return equals(t);}
	// DEBUG:
	void print(FILE * file = stdout, CSTR format = _T("%f"), CSTR start = NULL, CSTR separator = _T(", "), CSTR newLine = _T("\n"))
	{
		if(format)
		{
			int i,j;
			if(start)
				_ftprintf(file,start);
			for(i=0;i<rows;i++)
			{
				for(j=0;j<cols-1;j++)
				{
					_ftprintf(file,format,elems[i][j]);
					if(separator)
						_ftprintf(file,separator);
				}
				_ftprintf(file,format,elems[i][j]);
				if(newLine)
					_ftprintf(file,newLine);
			}
		}
	}
};

typedef Vector<_TCHAR> Chars;
class String
{
protected:
	int size,page;
	STR expand(int l);
	//operator int(){return length();}
public:
	Chars chars;
	BOOL shrink;
	// CONSTRUCTORS
	String():size(0),page(0),shrink(FALSE){}
	String(int length);
	String(const String &s);
	String(CSTR s);
	String(CSTR s1, const String& s2);
	String(const String& s1, CSTR s2);
	String(const String& s1, const String& s2);
	String(const String& s1, _TCHAR s2);
	String(_TCHAR s1, const String& s2);

	// METHODS
	STR setSize(int l);
	void destroy();
	void clear();

	int length() const {return size;}
	int remove(int idx = 0, int len = 1);
	int substr(STR str, int idx = 0, int len = -1);
	STR print(CSTR format = NULL, ...);
	size_t write(FILE *file)const;
	size_t read(FILE *file);

	int copy(CSTR s, int l);
	int copy(CSTR s)
	{
		if(!s) return 0;
		if(s==chars.elems) return size;
		return copy(s,(int)_tcsclen(s));
	}
	int copy(const String &s)
	{
		return copy(s.chars,s.size);
	}
	int copy(_TCHAR c)
	{
		return copy(&c,1);
	}

	BOOL equals(CSTR s, int l)const;
	BOOL equals(CSTR s)const
	{
		if(!s)return FALSE;
		return equals(s,(int)_tcsclen(s));
	}
	BOOL equals(const String &s)const
	{
		return equals(s.chars,s.size);
	}
	int equals(_TCHAR c)
	{
		return equals(&c,1);
	}

	int add(CSTR s, int l);
	int add(CSTR s)
	{
		if(!s)return FALSE;
		return add(s,(int)_tcsclen(s));
	}
	BOOL add(const String &s)
	{
		return add(s.chars,s.size);
	}
	int add(_TCHAR c)
	{
		return add(&c,1);
	}

	int insert(CSTR s, int idx, int len);
	int insert(CSTR s, int idx = 0)
	{
		if(!s)return FALSE;
		return insert(s,idx,(int)_tcsclen(s));
	}
	BOOL insert(const String &s, int idx = 0)
	{
		return insert(s.chars,idx,s.size);
	}
	int insert(_TCHAR c, int idx = 0)
	{
		return insert(&c,idx,1);
	}

	int indexOf(CSTR s, int idx, int len);
	int indexOf(CSTR s, int idx = 0)
	{
		if(!s||idx<0||idx>=size)return FALSE;
		STR str=_tcsstr(chars.elems+idx,s);
		return str?int(str-chars.elems):-1;
	}
	BOOL indexOf(const String &s, int idx = 0)
	{
		return indexOf(s.chars,idx,s.size);
	}
	int indexOf(_TCHAR c, int idx = 0)
	{
		if(idx<0||idx>=size)return FALSE;
		STR str=_tcschr(chars.elems+idx,c);
		return str?int(str-chars.elems):-1;
	}

	int lastIndexOf(CSTR s, int len);
	int lastIndexOf(CSTR s)
	{
		return s?lastIndexOf(s,int(_tcsclen(s))):-1;
	}
	BOOL lastIndexOf(const String &s)
	{
		return lastIndexOf(s.chars,s.size);
	}
	int lastIndexOf(_TCHAR c)
	{
		STR str=_tcsrchr(chars.elems,c);
		return str?int(str-chars.elems):-1;
	}
	
	// OPERATORS
	operator STR(){return chars.elems;}
	operator CSTR()const{return chars.elems;}
	CSTR toString()const{return chars.elems;}

	String& operator =(_TCHAR s){copy(s);return *this;}
	String& operator =(CSTR s){copy(s);return *this;}
	String& operator =(const String& s){copy(s);return *this;}

	String& operator +=(_TCHAR s){add(s);return *this;}
	String& operator +=(CSTR s){add(s);return *this;}
	String& operator +=(const String& s){add(s);return *this;}

	BOOL operator ==(_TCHAR s)const{return equals(s);}
	BOOL operator ==(CSTR s)const{return equals(s);}
	BOOL operator ==(const String& s)const{return equals(s);}

	BOOL operator !=(_TCHAR s)const{return equals(s);}
	BOOL operator !=(CSTR s)const{return !equals(s);}
	BOOL operator !=(const String& s)const{return !equals(s);}

	// s1 + s2
#define _STRING_PLUS(T1,T2)friend String operator +(T1 s1, T2 s2){String tmp(s1,s2);return tmp;} 
	_STRING_PLUS(CSTR,const String&)
	_STRING_PLUS(const String&,CSTR)
	_STRING_PLUS(const String&,const String&)
	_STRING_PLUS(_TCHAR,const String&)
	_STRING_PLUS(const String&,_TCHAR)
};
struct Info
{
	String name,value;
};
class InitInfo
{
protected:
	String buff;
public:
	List<Info> infos;
	int indexOf(CSTR name);
	Info* findInfo(CSTR name);
	NumberFormat format;
	BOOL getError;
	InitInfo():getError(FALSE)
	{
		format.exposant=TRUE;
	}
	Info* add(CSTR name, CSTR value = NULL);
	Info* addDouble(CSTR name, double value, CSTR fmt = NULL)
	{
		return add(name,format.toString(value,fmt));
	}
	Info* addInt(CSTR name, int value, CSTR fmt = NULL)
	{
		return add(name,format.toString(value,fmt));
	}
	Info* set(CSTR name, CSTR value);
	Info* setDouble(CSTR name, double value)
	{
		return set(name,format.toString(value));
	}
	Info* setInt(CSTR name, int value)
	{
		return set(name,format.toString(value));
	}
	CSTR get(CSTR name, CSTR defValue = _T(""));
	double getDouble(CSTR name, double defValue = 0.0, CSTR fmt = NULL)
	{
		return format.toDouble(get(name), defValue, fmt);
	}
	int getInt(CSTR name, int defValue = 0, CSTR fmt = NULL)
	{
		return format.toInt(get(name), defValue, fmt);
	}
	void destroy();
	BOOL read(CSTR fileName = _T("config.ini"), CSTR seps = _T("="), CSTR lines = _T("\n;"), CSTR blanks = _T("\t"))
	{
		File file;
		if(!file.open(fileName,_T("rt")))
			return FALSE;
		return read(file,seps,lines,blanks);
	}
	BOOL read(FILE *file, CSTR seps = _T("="), CSTR lines = _T("\n;"), CSTR blanks = _T("\t"));
	BOOL readStr(CSTR str, CSTR seps = _T("="), CSTR lines = _T("\n;"), CSTR blanks = _T("\t"));

	BOOL write(CSTR fileName = _T("config.ini"), CSTR sep = _T("="), CSTR line = _T("\n"))
	{
		File file;
		return (file.open(fileName,_T("wt")))?write(file,sep,line):FALSE;
	}
	BOOL write(FILE *file, CSTR sep = _T("="), CSTR line = _T("\n"));
	CSTR writeStr(CSTR sep = _T("="), CSTR line = _T("\n"));
	CSTR toString() {return buff.chars;}
};

class Tokenizer:public List<String>
{
public:
	Tokenizer(){}
	Tokenizer(CSTR s, CSTR separator = _T(","), CSTR blanks = NULL, BOOL clear = TRUE)
	{
		set(s,separator,blanks,clear);
	}
	BOOL set(CSTR s = _T(""), CSTR separator = _T(","), CSTR blanks = NULL, BOOL clear = TRUE);
};

#define MAX_PENDING_THREADS 1024
class ThreadInfo;
class Runnable
{
public:
	virtual DWORD run(const ThreadInfo *){return 0;}
	static BOOL start(const ThreadInfo *info);
};
class ThreadInfo
{
public:
	Runnable *caller;
	int priority;
	LPCSTR name;
	ThreadInfo():caller(NULL),name(""),priority(THREAD_PRIORITY_NORMAL)
	{}
	ThreadInfo(Runnable *caller, LPCSTR name = "", int priority = THREAD_PRIORITY_NORMAL):caller(caller),name(name),priority(priority)
	{}
	void start()
	{
		if(caller)
			caller->start(this);
	}
};

#define ZIP_HEADER_TAG 'GZIP'
#define ZIP_HEADER_SIZE 8
class Compressor:public Vector<BYTE>
{
public:
	BOOL compress(const BYTE *vector, int length);
	BOOL compress(const Vector<BYTE> &vector)
	{
		return compress(vector.elems,vector.size);
	}
	BOOL decompress(const BYTE *vector, int length);
	BOOL decompress(const Vector<BYTE> &vector)
	{
		return decompress(vector.elems,vector.size);
	}
};

class FileDialog;
class FileFilter
{
	friend class FileDialog;
protected:
	String ext,info;
public:
	
	FileFilter(CSTR ext0 = NULL, CSTR info0 = NULL){init(ext0,info0);}
	void init(CSTR ext0 = NULL, CSTR info0 = NULL)
	{
		if(!ext0)
		{
			ext=_T("*");
			info=_T("All Files");
		}
		else
		{
			ext=ext0;
			if(info0)
				info=info0;
			else
				info=_T("");
		}
	}
	CSTR getExt() const {return ext;}
	CSTR getInfo() const {return info;}
};
class FileDialog
{
protected:
	OPENFILENAME ofn;
	void fill(OPENFILENAME *ofn, DWORD flags = 0);
	void parse();
	typedef List<FileFilter>::Object FilterObj;
	FileFilter *filter;
	String filterStr;
public:
	_TCHAR path[MAX_PATH],shortPath[MAX_PATH];
	String
		defExt,
		fileDir,
		fileName,
		fileExt,
		nameOnly;
	_TCHAR drive;
	File file;
	List<FileFilter> filters;
	DWORD filterIndex;
	HWND hWnd;

	FileDialog();
	BOOL addFilter(CSTR ext = NULL, CSTR info = NULL);
	CSTR open();
	CSTR save(CSTR filePath = NULL);
	CSTR getFilterStr();
	FILE * openFile(CSTR mode);
	FILE * readFile(BOOL text = FALSE){return openFile(text?_T("rt"):_T("rb"));}
	FILE * writeFile(BOOL text = FALSE){return openFile(text?_T("wt"):_T("wb"));}
	FILE * appendFile(BOOL text = FALSE){return openFile(text?_T("at"):_T("ab"));}
	void closeFile() {file.close();}
	CSTR getDir() const {return fileDir;}
	CSTR getExt() const {return fileExt;}
	CSTR getName() const {return fileName;}
	const OPENFILENAME& getFileInfo()const{return ofn;}
};
class Directory
{
	HANDLE h;
	String name;
	void reset()
	{
		if(h)
		{
			FindClose(h);
			h=NULL;
		}
	}
public:
	WIN32_FIND_DATA data;
	Directory():h(NULL){}
	~Directory(){reset();}
	void set(CSTR path = NULL);
	CSTR next();
	static BOOL getCurrent(String &current);
	static BOOL setCurrent(CSTR current);
};

class Application:public Listener, public Runnable
{
private:
	static Application *current;
	BOOL created;
	static LRESULT CALLBACK WinProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
	BOOL isProgramLooping, isUpdated;
	void initWindow();
	BOOL createWindow();
	BOOL destroyWindow();
	BOOL registerClass();
	PrecisionTimer loopTimer;
protected:
	HINSTANCE hInst,hPrevInst;
	HANDLE process;
	HCURSOR cursor;
	BOOL createFullScreen;
	static DWORD msgCount;
	String name;
	LPCTSTR cmdLine;
	Tokenizer cmdToken;
	int cmdShow;
	Display display;
	Window window;
	Console console;
	Mouse mouse;
	Keyboard keyboard;
	MSG msg;			// Message Info
	float loopTime,runTime;
	// To Be Defined:
	virtual BOOL create(){return TRUE;}
	virtual BOOL glCreate(){return TRUE;}
	virtual void glDestroy(){}
	virtual void destroy(){}
	virtual void update(){}
	virtual void draw() = NULL;
	virtual void reshape() = NULL;
	virtual BOOL callback()
	{
		return FALSE;
	}
	virtual BOOL input(BYTE)
	{
		if(msg.message==WM_SYSKEYDOWN&&keyboard.code==VK_F4)
		{
			terminate();
			return TRUE;
		}
		return FALSE;
	}
public:
	BOOL allowSleep,allowPower,allowYield;
	Application();
	~Application();
	static Application *getCurrent();
	void wake()
	{
		SendMessage(window.hWnd,WM_USER_WAKE,0,0);
	}
	void redraw()
	{
		draw();
		SwapBuffers(window.hDC);
	}
	void toggleFullscreen()
	{
		createFullScreen=!createFullScreen;
		PostMessage (window.hWnd,WM_QUIT,0,0);
	}
	void setFullscreen(BOOL full)
	{
		if(full!=createFullScreen)
		{
			createFullScreen=full;
			PostMessage (window.hWnd,WM_QUIT,0,0);
		}
	}
	void terminate()
	{
		isProgramLooping = FALSE;
		PostMessage (window.hWnd,WM_QUIT,0,0);
	}
	HANDLE getProcess() const {return process;}
	BOOL setProcessPriority (DWORD priority)
	{
		return SetPriorityClass(process,priority);
	}
	DWORD getProcessPriority () const
	{
		return GetPriorityClass(process);
	}
	BOOL resetWindow();
	BOOL resizeWindow(int newWidth, int newHeight)
	{
		window.width=newWidth;
		window.height=newHeight;
		return resetWindow();
	}
	BOOL moveWindow(int newLeft, int newTop)
	{
		window.left=newLeft;
		window.top=newTop;
		return resetWindow();
	}
	void setUpdated(BOOL value)
	{
		isUpdated=isUpdated||value;
	}
	int main (	HINSTANCE hInstance, HINSTANCE hPrevInstance,
				LPCTSTR lpCmdLine, int nCmdShow);
};

}; // namespace Lio

#endif //_LIOBASE_H